#include "../log.h"
#include <stdio.h>
#include <time.h>
#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>

static struct {
    int level;
    int mode;
    FILE *file;
    char *file_name_prefix;
    int file_size_limit;
    int file_name_index;
    int file_size;
}log = {LOG_NULL, MODE_NULL, NULL, NULL, 0};

//  set file pointer
//  open the file according to
//  file_name_prefix and file_name_index
void set_file_pointer(){
    char file_name[50];
    sprintf(file_name, "%s%03d", \
        log.file_name_prefix, log.file_name_index);
    log.file = fopen(file_name, "w");
}


//  set log level to level.
//  returns the previouse level.
//  the default level is LOG_NULL.
int log_set_level(int level){
    int pre_level = log.level;
    log.level = level;
    return pre_level;
}

//  returns the current level.
int log_get_level(){
    return log.level;
}

//  set log mode to mode.
//  @param mode the mode
//  @param file the file handler to write log into, NULL if not used
//  @param file_name_prefix the file name prefix for the files to write log into, NULL if not used
//  @param file_size_limit the limitation to the log file, at which a new file should be created, 0 if not used
//  returns the previouse mode.
//  the default mode is MODE_NULL.
int log_set_mode(int mode, FILE* file, const char* file_name_prefix, int file_size_limit){
    int pre_mode = log.mode;
    log.mode = mode;
    // mode MODE_FILE_HANDLER
    if (mode == MODE_FILE_HANDLER)
        log.file = file;
    // mode MODE_FILE_SIZE_LIMITED
    if (mode == MODE_FILE_SIZE_LIMITED){
        if (log.file_name_prefix != NULL)
            free(log.file_name_prefix);
        log.file_name_prefix = (char*)malloc( \
            strlen(file_name_prefix + 1));
        strcpy(log.file_name_prefix, file_name_prefix);
        log.file_size_limit = file_size_limit;
        log.file_name_index = 0;
        set_file_pointer();
    }
    return  pre_mode;
}

//  returns the current mode.
int log_get_mode(){
    return log.mode;
}


//  format the message to 
//  2019-11-02 12:03:23 - the message
//  @param format the message
char* format_messgae(const char* format){
    // free the last message
    static char *last_message = NULL;
    if (last_message != NULL)
        free(last_message);

    // get time
    char *message = (char*)malloc(strlen(format) + 40);
    time_t t_time = time(NULL);
    struct tm *st_time;

    // format the message
    st_time = localtime(&t_time);
    sprintf(message, "%d-%02d-%02d %02d:%02d:%02d - %s\n", \
        st_time->tm_year+1900, st_time->tm_mon+1, st_time->tm_mday, \
        st_time->tm_hour, st_time->tm_min, st_time->tm_sec, \
        format);
    last_message = message;

    return message;
}

//  write a message into the output device
//  a timestamp is to be added to the message, formated as:
//  2019-11-02 12:03:23 - the message
//  the time showed here is UTC time.
//  returns the number of characters written to the device, 
//  including the \n character(s).
int log_write(int level, const char* format, ...){

    va_list valist;
    va_start(valist, format);
    char *detail = (char*)malloc(strlen(format)+20);
    vsprintf(detail, format, valist);
    va_end(valist);
    char *message = format_messgae(detail);

    // mode file_size_limited
    if (log.mode == MODE_FILE_SIZE_LIMITED){
        if (strlen(message) + log.file_size < log.file_size_limit){
            fprintf(log.file, message);
            log.file_size += strlen(message);
        }
        else{
            int rest = log.file_size_limit - log.file_size;
            char *front = (char*)malloc(rest + 1);
            strncpy(front, message, rest);
            front[rest] = 0;
            fprintf(log.file, front);
            log_close();

            log.file_size = 0;
            log.file_name_index ++;
            set_file_pointer();
            log_write(level, message + rest);
        }
    }
    // mode file_handler
    else if (log.mode == MODE_FILE_HANDLER){
        fprintf(log.file, message);
    }

    free(detail);
    return strlen(message);
}

//  finish and close the log output.
void log_close(){
    fclose(log.file);
    log.file = NULL;
}